/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.github.glomadrian.codeinputlib;

import com.github.glomadrian.codeinputlib.data.FixedStack;
import com.github.glomadrian.codeinputlib.model.Underline;

import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.TextField;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.global.resource.ResourceManager;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.multimodalinput.event.KeyEvent;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

public class CodeInput extends TextField implements Component.KeyEventListener {
    private final List<String> contentText = new ArrayList<>();
    private static final int textSize = 100;
    private static final int textSizeN = 50;

    private static final int LABLENO = 0x000110;
    private HiLogLabel label = new HiLogLabel(HiLog.LOG_APP, LABLENO, "CodeInput_");
    private HiLogLabel label1 = new HiLogLabel(HiLog.LOG_APP, LABLENO, "CodeInput1_");
    private static final int DEFAULT_CODES = 6;
    private static final String KEYCODE = "KEYCODE_";
    private static final Pattern KEYCODE_PATTERN = Pattern.compile(KEYCODE + "(\\w" + ")");
    private FixedStack<Character> characters;

    /**
     * 输入框的宽度
     */
    private int mEtWidth;
    private int underlineAmount;
    private int underlineColor;
    private int underlineSelectedColor;
    private int hintColor;
    private String hintTextStr;
    private int textColor;

    private ValueAnimator focusAnim;
    private ValueAnimator unFocusAnim;
    private static final int hintMaxTranslationY = 90;// max translationY of hint text
    private int hintCurTranslationY = 0;
    private int hintCurSize = textSizeN;
    private static final float maxLineOffset = 12;// max underline scale size
    private float curLineOffset = 0;
    private static final float minTextScale = 0.8f;// min text size scale of hint text

    private final List<Underline> underLines = new ArrayList<>();

    private static final int textUnderLineOffset = 20;
    private int curInputIndex = 0;
    private char[] Mchars;

    private final Paint textPaint = new Paint();

    {
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(textSizeN);
        textPaint.setColor(Color.RED);
    }

    private final Paint underLinePaint = new Paint();
    {
        underLinePaint.setAntiAlias(true);
        underLinePaint.setStyle(Paint.Style.STROKE_STYLE);
        underLinePaint.setStrokeWidth(4);
        underLinePaint.setColor(Color.RED);
    }

    public CodeInput(Context context) {
        super(context);
    }

    public CodeInput(Context context, AttrSet attrSet) {
        super(context, attrSet);
        this.listener = null;
        init(attrSet);
    }

    public CodeInput(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        this.listener = null;
        init(attrSet);
    }

    private void initDataStructures() {
        characters = new FixedStack();
        characters.setMaxSize(underlineAmount);
    }

    private void init(AttrSet attributeset) {
        setBubbleHeight(0);
        setBubbleWidth(0);
        initDefaultAttributes();
        initCustomAttributes(attributeset);
        initDataStructures();
        // init anim
        focusAnim = ValueAnimator.ofFloat(0, 1);
        focusAnim.setDuration(300);
        focusAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(AnimatorValue animatorValue, float fraction, Object animatedValue) {
                float value = (float) animatedValue;
                hintCurTranslationY = (int) (hintMaxTranslationY * value);

                hintCurSize = (int) Math.max((int) (textSizeN * (1 - value)), textSizeN * minTextScale);
                hintCurSize = Math.min(textSizeN, hintCurSize);

                curLineOffset = fraction * maxLineOffset;

                for (int i = 0; i < underLines.size(); i++) {
                    Underline underLine = underLines.get(i);
                    HiLog.info(label1, "onAnima before start: " + underLine.start + " ,end: " + underLine.end
                        + " curLineOffset: " + curLineOffset);
                    underLine.start = underLine.left + curLineOffset;
                    underLine.end = underLine.right - curLineOffset;
                    HiLog.info(label, "onAnima after start: " + underLine.start + " ,end: " + underLine.end);
                }
                invalidate();
            }
        });

        unFocusAnim = ValueAnimator.ofFloat(1, 0);
        unFocusAnim.setDuration(300);
        unFocusAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(AnimatorValue animatorValue, float fraction, Object animatedValue) {
                float value = (float) animatedValue;
                hintCurTranslationY = (int) (hintMaxTranslationY * value);

                hintCurSize = (int) Math.max((int) (textSizeN * (1 - value)), textSizeN * minTextScale);
                hintCurSize = Math.min(textSizeN, hintCurSize);
                curLineOffset = value * maxLineOffset;
                for (int i = 0; i < underLines.size(); i++) {
                    Underline underLine = underLines.get(i);
                    HiLog.info(label1, "onAnima before start: " + underLine.start + " ,end: " + underLine.end
                        + " curLineOffset: " + curLineOffset);
                    underLine.start = underLine.left + curLineOffset;
                    underLine.end = underLine.right - curLineOffset;

                    HiLog.info(label, "onAnima after start: " + underLine.start + " ,end: " + underLine.end);
                }
                invalidate();
            }
        });
        // prepare draw
        addDrawTask(new DrawTask() {
            @Override
            public void onDraw(Component component, Canvas canvas) {
                final int baseLine = getHeight() - textSize;
                final int textBaseLine = getHeight() - textSize - textUnderLineOffset;
                final float paddingLeft = 50f;
                if (underLines.isEmpty()) {
                    int preWidth = mEtWidth;
                    HiLog.info(label,"onDraw " + underlineAmount + ", mEtWidth: " + mEtWidth
                    + ", preWidth " + preWidth);
                    for (int i = 0; i < underlineAmount; i++) {
                        Underline underLine = new Underline();
                        underLine.left = preWidth * i + paddingLeft ;
                        underLine.right = underLine.left + preWidth ;
                        underLine.start = underLine.left;
                        underLine.end = underLine.right;
                        underLines.add(underLine);
                        Logger.w("init underLine = " + underLine);
                    }
                }
                // 1. draw overlay
                canvas.drawColor(Color.WHITE.getValue(), Canvas.PorterDuffMode.SRC_ATOP);
                // 2. draw hint anim
                textPaint.setColor(new Color(hintColor));
                textPaint.setTextSize(hintCurSize);
                canvas.drawText(textPaint, hintTextStr, paddingLeft, textBaseLine - hintCurTranslationY-hintCurSize/2);
                // 3. draw under line anim
                for (int i = 0; i < underLines.size(); i++) {
                    Underline underLine = underLines.get(i);
                    Logger.d("draw underLine = " + underLine);
                    if (isFocused() || curInputIndex > 0) {
                        if (curInputIndex == i) {
                            underLinePaint.setColor(new Color(underlineSelectedColor));
                        } else {
                            underLinePaint.setColor(new Color(underlineColor));
                        }
                    } else {
                        underLinePaint.setColor(new Color(underlineColor));
                    }
                    HiLog.info(label,"onDrawLine, start: " + underLine.start + ", end: " + underLine.end);
                    canvas.drawLine(underLine.start, baseLine, underLine.end, baseLine, underLinePaint);
                    if (i < contentText.size()) {
                        String charAt = contentText.get(i);
                        Logger.d("draw text = " + charAt);
                        textPaint.setColor(new Color(textColor));
                        if (charAt != null) {
                      //     canvas.drawText(textPaint, charAt, underLine.start + (underLine.end - underLine.start - textSize + (AttrHelper.vp2px(10,getContext()) / 2)), textBaseLine -hintCurSize/2);
                            canvas.drawText(textPaint, charAt, underLine.start + (underLine.end - underLine.start- AttrHelper.vp2px(10,getContext()))/2, textBaseLine - AttrHelper.vp2px(8,getContext()));
                        }
                    }
                }
            }
        }, DrawTask.BETWEEN_CONTENT_AND_FOREGROUND);
        addTextObserver(new TextObserver() {
            @Override
            public void onTextUpdated(String s, int i, int i1, int i2) {
                String text = getText();
                Logger.w("text="+text+", size1="+contentText.size()+", size2="+underlineAmount
                +", size3="+underLines.size());
                if (characters.size() > underlineAmount) {
                    return;
                }
                char[] chars = text.toCharArray();
                Mchars = chars;
                contentText.clear();

                for(int j = characters.size(); j>=0; j--){
                    if (characters.size()!=0){
                        characters.pop();
                    }
                }
                for (char c : chars) {
                    if (isLetterDigit(c+"")){
                        if (c >'Z'){
                            c  -=  32;
                        }
                        contentText.add(String.valueOf(c));
                        characters.push(c);
                    }
                }

                if (characters.size() >= underlineAmount){
                    if (listener != null){
                        listener.onCodeReady(getCode());
                    }
                }

                curInputIndex = contentText.size();
                invalidate();
            }
        });
        // observe focus changed
        setFocusChangedListener(new FocusChangedListener() {
            @Override
            public void onFocusChange(Component component, boolean gainFocus) {
                if (curInputIndex == 0) {
                    if (gainFocus) {
                        focusAnim.start();
                    } else {
                        unFocusAnim.start();
                    }
                    invalidate();
                }
            }
        });
    }

    private boolean isLetterDigit(String str) {
        String regex = "^[a-z0-9A-Z]+$";
        return str.matches(regex);
    }

    private void initDefaultAttributes() {
        ResourceManager resourceManager = getResourceManager();
        try {
            underlineColor = resourceManager.getElement(ResourceTable.Color_underline_default_color).getColor();
            underlineSelectedColor = resourceManager.getElement(ResourceTable.Color_underline_selected_color).getColor();
            hintColor = resourceManager.getElement(ResourceTable.Color_hintColor).getColor();
            textColor = resourceManager.getElement(ResourceTable.Color_textColor).getColor();
        } catch (Exception e) {
            e.printStackTrace();
        }
        underlineAmount = DEFAULT_CODES;
    }

    private codeReadyListener listener;;

    public void setCodeReadyListener(codeReadyListener listener) {
        this.listener = listener;
    }

    @Override
    public boolean onKeyEvent(Component component, KeyEvent keyEvent) {
        if (keyEvent.getKeyCode() == KeyEvent.KEY_DEL && keyEvent.isKeyDown()) {
            onKeyDelete();
            return true;
        }
        return false;
    }

    // 监听删除
    private void onKeyDelete() {
      for (int i = Mchars.length -1 ; i >= 0; i--){
          if (characters.size()!=0){
              characters.pop();
          }
      }

    }

    public interface codeReadyListener {
        // These methods are the different events and
        // need to pass relevant arguments related to the event triggered
        public void onCodeReady(Character[] code);

    }

    public Character[] getCode() {
        return characters.toArray(new Character[underlineAmount]);
    }


    private void initCustomAttributes(AttrSet attributeset) {
        underlineColor = Color.getIntColor(attributeset.getAttr("underline_color").get().getStringValue());
        underlineSelectedColor = Color.getIntColor(attributeset.getAttr("underline_selected_color").get().getStringValue());
        mEtWidth = AttrHelper.vp2px(attributeset.getAttr("mEtWidth").get().getIntegerValue(), getContext());
        hintTextStr = attributeset.getAttr("hint1").get().getStringValue();
        underlineAmount = attributeset.getAttr("codes").get().getIntegerValue();
        hintColor = Color.getIntColor(attributeset.getAttr("hint_color").get().getStringValue());
        textColor = Color.getIntColor(attributeset.getAttr("text_color").get().getStringValue());

    }

}
